#include "cwgenerator.h"

CWGenerator::CWGenerator(QObject *parent) :
    QObject(parent)
{
    aoInit();
    _stop_flag = false;
    cur_pos = 0;
    tone_freq = 800;
    tone_volume = 0.65;
    dash_dot = 3;
    sign_pause = 3;
    word_pause = 7;
    ttype = TT_SINUS;
    setCWSpeedWPM(6);
}

CWGenerator::~CWGenerator()
{
    stop();
    aoShutdown();
}

void CWGenerator::setCWFreq(double freq)
{
    tone_freq = freq;
}

void CWGenerator::setCWSpeed(int sign_per_sec)
{
    setCWSpeedWPM(sign_per_sec/5);
}

void CWGenerator::setCWSpeedWPM(int wpm)
{
    dot_t = 60.0/(50.0*(double)wpm);
}

void CWGenerator::setDashDuration(int dots)
{
    if(dots > 0)
        dash_dot = dots;
}

void CWGenerator::setSignPause(int dots)
{
    if(dots > 0)
        sign_pause = dots;
}

void CWGenerator::setWordPause(int dots)
{
    if(dots > 0)
        word_pause = dots;
}

void CWGenerator::setVolume(double volume)
{
    tone_volume = volume;
}

bool CWGenerator::loadCode(const QString &file)
{
    QFile fl(file);
    if(!fl.open(QFile::ReadOnly))
    {
        qDebug()<<"CWGenerator::err::open codefile '"<<file<<"'";
        return false;
    }
    while(!fl.atEnd())
    {
        QString src = fl.readLine(1024);
        src = src.replace(QRegExp("[\r\n]"),"").toLower();

        QStringList pair = src.split(" ");
        if(pair.size() < 2)
            continue;

        cwmap.insert(pair.value(0),pair.value(1));
    }
    return true;
}

void CWGenerator::setToneType(CWGenerator::ToneType type)
{
    ttype = type;
}

void CWGenerator::genCWText(const QString &text, int pos)
{
    emit startTextGen();
    _stop_flag = false;
    for(int i = pos; i < text.size(); ++i)
    {
        if(cur_pos < 0)
        {
            cur_pos = i;
            break;
        }

        if(_stop_flag)
        {
            emit endTextGen();
            return;
        }

        QString cur = text.mid(i,1).toLower();
        if(cur=="$")
        {
            while(text.mid(++i,1)!=" " && i < text.size())
                cur += text.mid(i,1);
        }

        if(cur == " ")
        {
            genTone(50, dot_t * word_pause, 0);
            continue;
        }

        if(!cwmap.contains(cur))
        {
            qDebug()<<"CWGenerator::warning::can't cw code '"<<cur<<"'";
            continue;
        }
        genTone(50, dot_t * (double)sign_pause, 0);

        QString cwsgn = cwmap.value(cur);
        for(int y = 0; y < cwsgn.size(); ++y)
        {
            QString sgn = cwsgn.mid(y,1);
            if(sgn == "-" || sgn == "_")
                genDash();
            else if(sgn == ".")
                genDot();

            if(y < cwsgn.size()-1)
                genTone(50,dot_t,0);
        }
    }

    if(cur_pos > 0)
    {
        emit pauseTextGen(cur_pos);
        cur_pos = 0;
    }
    else
        emit endTextGen();
}

void CWGenerator::genTone(double freq, double time, double volume)
{
    if(ttype == TT_SINUS)
        genSinus(freq,time,volume);
    else if(ttype == TT_MEANDR)
        genMeandr(freq,time,volume);
}

void CWGenerator::stop()
{
    _stop_flag = true;
}

void CWGenerator::pause()
{
    cur_pos = -1;
}

void CWGenerator::genDot()
{
    genTone(tone_freq,dot_t,tone_volume);
}

void CWGenerator::genDash()
{
    genTone(tone_freq,dash_dot * dot_t,tone_volume);
}

void CWGenerator::genSinus(double freq, double time, double volume)
{
    int buf_size = static_cast<int>(fmt.bits/8 * fmt.channels * fmt.rate * time);
    char *buffer = new char[buf_size+1];
    int sample;
    int inter=8;
    double tm = fmt.rate * time;
    double dt = fmt.rate/1000*inter;
    double vm = 0;

    for (int i = 0; i < tm; i++)
    {
        for (int c = 0; c < fmt.channels; c++)
        {
            sample = static_cast<int>(volume * vm * (1 << (fmt.bits - 1)) * sin(2 * M_PI * freq * ((float) i/fmt.rate)));
            for (int j = 0; j < fmt.bits/8; j++)
            {
                buffer[fmt.bits/8*i*fmt.channels + fmt.channels*c + j] = sample & 0xff;
                sample >>= 8;
            }
        }
        //----сглаживание фронтов синусоиды---------
        if(i < dt) vm += 1.0/dt;
        else if(i > tm-dt) vm -= 1.0/dt;
        else vm = 1.0;
        if(vm < 0)vm = 0;
        //-------------------------------------------
    }

    for(int p = 0; p < 10 && !_stop_flag; ++p)
    {
        char *cur = buffer + fmt.bits/8*fmt.channels*((int)tm/10*p);
        int sz = buf_size/10;
        ao_play(dev, cur, sz);
    }

    delete[] buffer;
}

void CWGenerator::genMeandr(double freq, double time, double volume)
{
}

void CWGenerator::aoInit()
{
    ao_initialize();
    driver = ao_default_driver_id();
    memset(&fmt,0,sizeof(ao_sample_format));
    fmt.bits = 16;
    fmt.channels = 2;
    fmt.rate = 44100;
    fmt.byte_format = AO_FMT_LITTLE;

    dev = ao_open_live(driver,&fmt,NULL);
    if(!dev)
    {
        qDebug()<<"CWGenerator::err::open_ao_device";
        aoShutdown();
        return;
    }
}

void CWGenerator::aoShutdown()
{
    ao_close(dev);
    ao_shutdown();
}
